/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_orm.htm
 *
 * Zhiqim ORM is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.orm;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.zhiqim.kernel.Z;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.json.Jsons;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.model.maps.HashMapCV;
import org.zhiqim.kernel.model.maps.HashMapSS;
import org.zhiqim.kernel.model.maps.HashMapSV;
import org.zhiqim.kernel.model.maps.MapSS;
import org.zhiqim.kernel.schedule.ScheduleFactory;
import org.zhiqim.kernel.schedule.Task;
import org.zhiqim.kernel.schedule.TaskThreader;
import org.zhiqim.kernel.transaction.Transaction;
import org.zhiqim.kernel.transaction.TransactionManager;
import org.zhiqim.kernel.transaction.TransactionService;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Resources;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.orm.cache.ZTableCacheTask;
import org.zhiqim.orm.config.ZSQLConfig;
import org.zhiqim.orm.datasource.ZDataSource;
import org.zhiqim.orm.dbo.defined._Table;
import org.zhiqim.orm.impl.ZDDLImplement;
import org.zhiqim.orm.impl.ZSQLImplement;
import org.zhiqim.orm.impl.ZTableImplement;
import org.zhiqim.orm.impl.ZTablerImplement;
import org.zhiqim.orm.impl.ZTabImplement;
import org.zhiqim.orm.impl.ZViewImplement;
import org.zhiqim.orm.impl.ZViewrImplement;

/**
 * ORM服务器，实现系统定义的服务、多例接口，支持配置创建和手动创建<br><br>
 * 
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class ORMServer extends ORMPredefinded implements TransactionService, ORMConstants
{
    private static final Log log = LogFactory.getLog(ORMServer.class);
    
    private ORMParameter parameter;
    private ORMPolicy policy;
    private ZDataSource dataSource;

    /***********************************************************************************/
    //服务创建&销毁
    /***********************************************************************************/

    @Override /** 服务配置方式创建 */
    public boolean create() throws Exception
    {
        if (id == null)
            return false;
        
        Group group = Z.conf().group(id);
        if (group == null)
            return false;
        
        return create(new ORMParameter(group));
    }
    
    /**
     * 数据库映射服务构造函数，由业务创建，如果配置创建直接调用无参数构造函数
     * 
     * @param parameter     数据库参数
     * @throws Exception    创建异常
     */
    public boolean create(ORMParameter parameter) throws Exception
    {
        this.parameter = parameter;
        
        log.info("初始化[数据库映射:%s]开始...", id);
        
        //1.判断是否支持的数据库
        String dbType = parameter.getDbType();
        policy = ORMType.getDatabasePolicy(this, dbType);
        if (policy == null)
        {
            log.fatal("不支持的数据库类型[%s]", dbType);
            return false;
        }
        
        String result = policy.chkCreateBefore(parameter);
        if (result != null)
        {
            log.info("初始化[数据库映射:%s]%s", id, result);
            return false;
        }
        
        //2.初始化数据库连接池
        dataSource = parameter.newDatabase();
        if (!dataSource.open())
        {
            log.fatal("初始化连接池失败[%s]", dbType);
            return false;
        }
        
        //3.开启SQL处理器
        super.open();
        
        //4.初始化SQL数据
        if (!isInitSQLMapping())
        {
            log.fatal("初始化DBO和SQL失败[%s]", dbType);
            return false;
        }
        
        //5.创建指令对象，要注意创建顺序SQL-DDL-Tabler-Table-Tablee-View
        zSQL = new ZSQLImplement(this);
        zDDL = new ZDDLImplement(this);
        zTab = new ZTabImplement(this);
        zTable = new ZTableImplement(this);
        zTabler = new ZTablerImplement(this);
        zView = new ZViewImplement(this);
        zViewr = new ZViewrImplement(this);
        
        //6.初始化缓存
        initCacheConfig();
        
        //7.注册服务到事务管理中
        TransactionManager.addTransactionService(this);
        
        //8.检查配置的参数是否正确，（如MySQL保持时长要求少于数据库设置的时长，SQLite要求文件目录存在）
        result = policy.chkCreateAfter(parameter);
        if (result != null)
        {
            log.info("初始化[数据库映射:%s]%s", id, result);
            return false;
        }
        
        log.info("初始化[数据库映射:%s]完成!!!\r\n", id);
        return true;
    }

    @Override
    public void destroy() throws Exception
    {
        //从全局对象表和事务管理中移除
        TransactionManager.removeTransactionService(this);
        
        //关闭SQL处理器
        super.close();
        
        //销毁数据源
        if (dataSource != null)
        {
            dataSource.close();
            dataSource = null;
        }
    }
    
    /** 初始化所有.sql.xml文件数据 */
    private boolean isInitSQLMapping() throws Exception
    {
        HashMapSV<Integer> sqlConfigMap = parameter.getSqlConfig();
        for (Map.Entry<String, Integer> entry : sqlConfigMap.entrySet())
        {
            String key = entry.getKey();
            int type = entry.getValue();
            switch (type)
            {
            case Z_SQL_FILE_INT:
            {
                addSQLConfig(key, new FileInputStream(key));
                break;
            }
            case Z_SQL_FOLDER_INT:
            {
                File folder = new File(key);
                File[] fileList = folder.listFiles();
                for (File file : fileList)
                {
                    if (!file.isFile() || !file.canRead())
                        continue;
                    
                    String name = Files.toLinuxPath(file.getPath());
                    if (!name.endsWith(".sql.xml"))
                        continue;
                        
                    addSQLConfig(name, new FileInputStream(file));
                }
                
                break;
            }
            case Z_SQL_CLASS_INT:
            {
                addSQLConfig(key, Resources.getResourceStream(ORMServer.class, key));
                break;
            }
            case Z_SQL_PACKAGE_INT:
            {
                List<String> fileList = Resources.getResourceFileNameList(ORMServer.class, key);
                for (String name : fileList)
                {
                    if (!name.endsWith(".sql.xml"))
                        continue;
                        
                    String className = Strings.addEndsWith(key, "/") + name;
                    addSQLConfig(className, Resources.getResourceStream(ORMServer.class, className));
                }
                
                break;
            }
            }
        }
        
        return true;
    }
    
    /** 加载SQL到内存表中 */
    private void addSQLConfig(String name, InputStream is) throws Exception
    {
        ZSQLConfig sqlConfig = new ZSQLConfig(name);
        sqlConfig.load(is);
                
        for (Map.Entry<String, String> sqlEntry : sqlConfig.getSQLMap().entrySet())
        {
            if (sqlMap.containsKey(sqlEntry.getKey()))
                throw new ORMException("映射文件:[%s]配置异常，有相同的ID[%s]存在", name, sqlEntry.getKey());
            sqlMap.put(sqlEntry.getKey(), sqlEntry.getValue());
        }
        
        log.info("映射文件[%s]加载成功", name);
    }
    
    /** 初始化需要缓存的表加载数据到内存 */
    private void initCacheConfig() throws Exception
    {
        HashMapSS cacheConfigMap = parameter.getCacheConfig();
        for (String className : cacheConfigMap.keySet())
        {
            Class<?> clazz = Classes.forName(Z.cls().className(className));
            if (clazz == null)
                throw new ORMException("缓存类[%s]配置异常，未找到该类", className);
            
            _Table table = zTable.getTable(clazz);
            if (table == null)
                throw new ORMException("缓存类[%s]配置异常，该类不是标准表DBO对象", className);
            
            Task task = new ZTableCacheTask(zTable, clazz);
            MapSS paramMap = Jsons.toMapSS(cacheConfigMap.get(className));
            TaskThreader taskThreader = ScheduleFactory.getTaskThreader(task, paramMap);
            if (taskThreader == null)
                throw new ORMException("缓存类[%s]配置异常，时钟格式不正常", className);
            
            cacheMap.put(clazz, taskThreader);
        }
        
        zTable.schedule();
    }
    
    /***********************************************************************************/
    //获取服务的属性，参数&连接&缓存&事务等
    /***********************************************************************************/
    
    /** 获取数据库参数 */
    public ORMParameter getParameter()
    {
        return parameter;
    }
    
    /** 获取数据库类型 */
    public int getDatabaseType()
    {
        return parameter.getDatabaseType();
    }
    
    /** 获取数据库名称 */
    public String getDatabaseName()
    {
        return parameter.getDbName();
    }
    
    /** 是否打印更新日志 */
    public boolean isUpdateSqlLog()
    {
        return parameter.isUpdateSqlLog();
    }

    /** 是否打印查询日志 */
    public boolean isQuerySqlLog()
    {
        return parameter.isQuerySqlLog();
    }
    
    /** 判断是否是Oracle数据库 */
    public boolean isOracle()
    {
        return parameter.isOracle();
    }
    
    /** 判断是否是Microsoft SQLServer数据库 */
    public boolean isMssql()
    {
        return parameter.isMssql();
    }
    
    /** 判断是否是SQLite数据库 */
    public boolean isSqlite()
    {
        return parameter.isSqlite();
    }
    
    /** 判断是否是MySql数据库 */
    public boolean isMysql()
    {
        return parameter.isMysql();
    }

    /** 获取一个数据库资源 */
    public ZDataSource getDateSource()
    {
        return dataSource;
    }
    
    /** 获取一个连接 */
    public Connection getConnection() throws SQLException
    {
        return dataSource.getConnection();
    }
    
    /** 获取ORM不同数据库的策略封装对象 */
    public ORMPolicy getPolicy()
    {
        return policy;
    }
    
    /** 判断表是否是缓存表 */
    public boolean isCache(Class<?> clazz)
    {
        return cacheMap.containsKey(clazz);
    }
    
    /** 获取缓存表 */
    public HashMapCV<TaskThreader> getCacheMap()
    {
        return cacheMap;
    }

    @Override /** 获取事务实例 */
    public Connection getTransactionInstance() throws SQLException
    {
        return getConnection();
    }

    @Override /** 开始事务实例 */
    public Transaction beginTransaction(Object instance) throws SQLException
    {
        return new ORMTransaction(id, (Connection)instance).begin();
    }
}
